const isObject = obj => obj !== null && typeof obj === 'object'
const convert = target => isObject(target) ? reactive(target) : target
const hasOwnProperty = (target, key) => target.hasOwnProperty(key)
export function reactive (target) {
    if (!isObject(target)) return target

    const handler = {
        get(target, key, receiver) {
            // 收集依赖
            track(target, key)
            const result = Reflect.get(target, key, receiver)
            return convert(result)
        },
        set(target, key, value, receiver) {
            const oldValue = Reflect.get(target, key, receiver)
            let result = true
            if (value !== oldValue) {
                result = Reflect.set(target, key, value, receiver)
                console.log('set', result)
                if (result) {
                    // 触发更新
                    trigger(target, key)
                }
            }
            return result
        },
        deleteProperty(target, key) {
            const hasKey = hasOwnProperty(target, key)
            const result = Reflect.deleteProperty(target, key)
            if (hasKey && result) {
                console.log('删除操作触发')
                // 触发更新
                trigger(target, key)
            }
            return result
        }
    }

    return new Proxy(target, handler)
}


let activeEffect = null
export function effect(callback) {
    activeEffect = callback
    callback()
    activeEffect = null
}

// 收集依赖
const targetMap = new WeakMap()
export function track(target, key) {
    if (!activeEffect) return
    let depsMap = targetMap.get(target)
    if (!depsMap) {
        targetMap.set(target, depsMap = new Map())
    }
    let dep = depsMap.get(key)
    if (!dep) {
        depsMap.set(key, dep = new Set())
    }
    dep.add(activeEffect)
}

export function trigger (target, key) {
    const depsMap = targetMap.get(target)
    if (!depsMap) return
    const dep = depsMap.get(key)
    if (dep) {
        dep.forEach(effect => {
            effect()
        });
    }
}

export function ref(raw) {
    if (isObject(raw) &&  raw.__is_ref) {
        return raw
    }
    let value = convert(raw)
    const r = {
        __is_ref: true,
        get value() {
            track(r, 'value')
            return value
        },
        set value(newValue) {
            if (value !== newValue) {
                // raw = newValue
                value = convert(newValue)
                trigger(r, 'value')
            }
        }
    }
    return r
}

export function toRefs(proxy) {
    const ret = proxy instanceof Array ?  new Array(proxy.length) : {}
    for (const key in proxy) {
        ret[key] = toProxyRef(proxy, key)
    }
    return ret
}

function toProxyRef(proxy, key) {
    const r = {
        __is_ref: true,
        get value() {
            return proxy[key]
        },
        set value(newValue) {
            proxy[key] = newValue
        }
    }
    return r
}

export function computed(getter) {
    const result = ref()
    effect(() => {
        result.value = getter()
    })
    return result
}